using System.Collections.Generic;
using System.Linq;

namespace DataAssociations.AssociationMiningAlgorithms.FpTree
{
    internal class FPGTree
    {
        private Node _rootNode;

        public Node RootNode
        {
            get { return _rootNode; }
        }

        public FPGTree(IEnumerable<List<string>> associationSets)
        {
            AddRootNode();
            foreach (var associationSet in associationSets)
                RegisterAssociationSet(associationSet);
        }

        private void RegisterAssociationSet(List<string> associationSet)
        {
            Node currentNode = _rootNode;
            TraverseFPGTree(currentNode, associationSet, 0);
        }

        private void TraverseFPGTree(Node currentNode, List<string> associationSet, int currentElement)
        {
            if (associationSet.Count == currentElement)
                return;
            if (currentNode.ChildLabels.Contains(associationSet[currentElement]))
            {
                var targetChildNode = currentNode.Children.First(x => x.Label == associationSet[currentElement]);
                targetChildNode.Counter++;
                TraverseFPGTree(targetChildNode, associationSet, ++currentElement);
            }
            else
            {
                for (int i = currentElement; i < associationSet.Count; i++)
                {
                    var newNode = new Node(associationSet[i], 1, new List<Node>());
                    currentNode.AddChild(newNode);
                    currentNode = newNode;
                }
            }
        }

        private void AddRootNode()
        {
            _rootNode = new Node("ROOT", -1, new List<Node>());
        }

        public Dictionary<string, List<List<Node>>> BuildHashedPathsToEachLeaf()
        {
            var hashedPaths = new Dictionary<string, List<List<Node>>>();
            foreach (var child in _rootNode.Children)
            {
                var hashedPathsFromChild = BuildPathsRecursively(child, new List<Node>(),
                                                                 new Dictionary<string, List<List<Node>>>());
                hashedPaths = hashedPaths.Concat(hashedPathsFromChild).ToDictionary(x => x.Key, x => x.Value);

            }
            return hashedPaths;
        }

        private Dictionary<string, List<List<Node>>> BuildPathsRecursively(Node currentNode, List<Node> path,
                                                                           Dictionary<string, List<List<Node>>>
                                                                               hashedPaths)
        {
            path.Add(currentNode);
            if (currentNode.Children.Count != 0)
            {
                for (int i = 0; i < currentNode.Children.Count; i++)
                {
                    BuildPathsRecursively(currentNode.Children[i], path, hashedPaths);
                }
            }
            if (currentNode.Label != path[0].Label)
            {
                var rootLeafPair = path[0].Label + ";"+ path.Last().Label;
                List<List<Node>> paths;
                if (!hashedPaths.TryGetValue(rootLeafPair, out paths))
                    hashedPaths.Add(rootLeafPair, new List<List<Node>> { new List<Node>(path) });
                else
                    hashedPaths[rootLeafPair].Add(new List<Node>(path));
            }
            path.Remove(currentNode);
            return hashedPaths;
        }
    }
}